#pragma once

namespace LSH
{

struct mdxFloat2
{
    float c[2];
};

struct mdxFloat3
{
    float c[3]; // components 
};

struct mdxFloat4
{
    float c[4];
};

struct mdxBoundingBox
{
    float radius;
    mdxFloat3 min;
    mdxFloat3 max;
};

struct mdxScalingTrack
{
    DWORD time;
    union 
    {
        DWORD TextrueId;
        float alpha;
        float c[3];
    };
    float inTan;
    float outTan;
};

struct MdxInfo
{
    char name[80];
    char path[MAX_PATH];
    mdxBoundingBox boundingBox;
    DWORD blendTime;
};

struct MdxSequence
{
    char    name[80];
    int     intervalStart;
    int     intervalEnd;
    float   moveSpeed;
    DWORD   nonLooping;
    float   rarity;
    DWORD   syncPoint;
    mdxBoundingBox boundingBox;
};

struct MdxSequenceSet
{
    Array<MdxSequence> sequences;
};

struct MdxGolbalSequence
{
    DWORD duration;
};

struct MdxGolbalSequenceSet
{
    Array<MdxGolbalSequence> golbalSequences;
};

struct MdxMaterialAlpha  //  DWORD 'KMTA';
{
    DWORD interpolationType;
    int GlobalSequenceId;

    Array<mdxScalingTrack> scalingTracks;

    int getSize()
    {
        int selfSize;
        selfSize = 3 * sizeof(DWORD);

        int count = scalingTracks.size();
        selfSize += count * (sizeof(DWORD) + sizeof(float));
        if(interpolationType > 1)
        {
            selfSize += count * 2 * sizeof(float);
        }
        return selfSize;
    }
};

struct MdxMaterialTextureId // DWORD 'KMTF';
{
    int interpolationType;

    DWORD globalSequenceId;

    Array<mdxScalingTrack> textrueScalingTarcks;
    
    int getSize()
    {
        int selfSize;
        selfSize = 3 * sizeof(DWORD);

        int count = textrueScalingTarcks.size();
        selfSize += count * 2 * sizeof(DWORD);
        if(interpolationType > 1)
        {
            selfSize += count * 2 * sizeof(float);
        }

        return selfSize;
    }
};

struct MdxMaterialLayer // DWORD 'LAYS';
{
    DWORD filterMode;
                        //0 - None
                        //1 - Transparent
                        //2 - Blend
                        //3 - Additive
                        //4 - AddAlpha
                        //5 - Modulate
                        //6 - Modulate2x
    DWORD shadingFlags;
    DWORD ShadingFlags;                
                        //#1   - Unshaded
                        //#2   - SphereEnvironmentMap
                        //#4   - ???
                        //#8   - ???
                        //#16  - TwoSided
                        //#32  - Unfogged
                        //#64  - NoDepthTest
                        //#128 - NoDepthSet
    DWORD textureId;
    int textureAnimationId;
    DWORD coordId;
    float alphaValue;

    MdxMaterialAlpha alpha;
    MdxMaterialTextureId textureIdTrack;
};

struct MdxMaterial
{
    enum eFlag {
        ConstantColor           = 1
        ,Unknown1               = ConstantColor << 1
        ,Unknown2               = ConstantColor << 2
        ,SortPrimitivesNearZ    = ConstantColor << 3
        ,SortPrimitivesFarZ     = ConstantColor << 4
        ,FullResolution         = ConstantColor << 5
    };
    DWORD priorityPlane;
    DWORD flags;

    Array<MdxMaterialLayer> layers;
};

struct MdxMaterialSet // DWORD 'MTLS';
{
    Array<MdxMaterial> materials;
};


struct MdxTexture // 'TEXS'
{
    DWORD   replaceableId;
    char	name[MAX_PATH];
    DWORD   flags;
};

struct MdxTextureSet
{
    Array<MdxTexture> textures;
};

struct mdxFace
{
    WORD index[3];
};

struct mdxExtent
{
    mdxFloat3 minimumExtent;
    mdxFloat3 maximumExtent;
};

struct MdxGeometric
{
    Array<mdxFloat3> vertexPositions;    // 'VRTX'
    Array<mdxFloat3> vertexNormals;      // 'NRMS'
    Array<DWORD> faceTypeGroups;        // 'PTYP'
    Array<DWORD> faceGroups;            // 'PCNT'
    Array<mdxFace> face;                // 'PVTX'
    Array<BYTE> vertexGroups;           // 'GNDX'
    Array<DWORD> matrixGroups;          // 'MTGC'
    Array<DWORD> matrixIndexes;         // 'MATS'

    DWORD materialId;
    DWORD selectionGroup;
    DWORD selectionFlags;           //0  - None
                                    //#1 - ???
                                    //#2 - ???
                                    //#4 - Unselectable
    mdxBoundingBox boundingBox;
    Array<mdxExtent> extents;
    DWORD numberOfTextureVertexGroups;        // 'UVAS'
    Array<mdxFloat2> vertexTexturePositions;  // 'UVBS'
};

struct MdxGeometricSet // 'GEOS'
{
    Array<MdxGeometric> geometrics;
};


struct MdxGeometAlpha
{
  //DWORD NrOfTracks;
  DWORD interpolationType;             //0 - None
                                       //1 - Linear
                                       //2 - Hermite
                                       //3 - Bezier
  DWORD GlobalSequenceId;

  Array<mdxScalingTrack> tracks;

  int getSize()
    {
        int selfSize;
        selfSize = 3 * sizeof(DWORD);

        int count = tracks.size();
        selfSize += count * (sizeof(DWORD) + sizeof(float));
        if(interpolationType > 1)
        {
            selfSize += count * 2 * sizeof(float);
        }

        return selfSize;
    }
};

struct  MdxGeometColor // KGAC
{
    // DWORD NrOfTracks;
    DWORD interpolationType;             //0 - None
                                       //1 - Linear
                                       //2 - Hermite
                                       //3 - Bezier
    DWORD globalSequenceId;

    Array<mdxScalingTrack>  tracks;

    int getSize()
    {
        int selfSize;
        selfSize = 3 * sizeof(DWORD);

        int count = tracks.size();
        selfSize += count * (sizeof(DWORD) + sizeof(mdxFloat3));
        if(interpolationType > 1)
        {
            selfSize += count * 2 * sizeof(float);
        }

        return selfSize;
    }
};

struct MdxGeometAnimation
{
    DWORD inclusiveSize;

    float Alpha;
    DWORD Flags;                       //#1 - DropShadow
                                       //#2 - Color
    mdxFloat3 color;
    DWORD GeosetId;

    Array<MdxGeometAlpha>   alphaTrackSet;
    Array<MdxGeometColor>   colorTrackSet;

    int getSize()
    {
        return inclusiveSize;
    }
};

struct MdxGeometAnimationSet
{
    Array<MdxGeometAnimation> animation;
};


struct MdxTranslationTrack
{
    DWORD Time;
    mdxFloat3 Translation;
    
    mdxFloat3 InTan;    // InterpolationType > 1
    mdxFloat3 OutTan;
    
};

struct MdxGeosetTranslation // // 'KGTR';
{
                                        // DWORD NrOfTracks;
    DWORD interpolationType;            //0 - None
                                        //1 - Linear
                                        //2 - Hermite
                                        //3 - Bezier
    DWORD globalSequenceId;
    Array<MdxTranslationTrack> tracks;

    int getSize()
    {
        int mySize = 0;
        mySize += 2 * sizeof(DWORD);
        int count = tracks.size();
        if(count > 0)
        {
            mySize += count * (sizeof(DWORD) + sizeof(mdxFloat3));
            if(interpolationType > 1)
            {
                mySize += count * 2 * sizeof(mdxFloat3);
            }
        }
        return mySize;
    }
};

struct MdxRotationTrack
{
    DWORD Time;
    mdxFloat4 Rotation;

    mdxFloat4 InTan;    // InterpolationType > 1
    mdxFloat4 OutTan;
};


struct MdxGeosetRotation // 'KGRT'
{
                                    // DWORD NrOfTracks;
    DWORD interpolationType;        //0 - None
                                    //1 - Linear
                                    //2 - Hermite
                                    //3 - Bezier
    DWORD globalSequenceId;
    Array<MdxRotationTrack> tracks;

    int getSize()
    {
        int mySize = 0;
        mySize += 2 * sizeof(DWORD);
        int count = tracks.size();
        mySize += count * (sizeof(DWORD) + sizeof(mdxFloat4));
        if(interpolationType > 1)
        {
            mySize += count * (2 * sizeof(mdxFloat4));
        }
        return mySize;
    }
};


struct MdxScalingTrack
{
    DWORD Time;
    mdxFloat3 Scaling;

    mdxFloat3 InTan;    // InterpolationType > 1
    mdxFloat3 OutTan;
};

struct MdxGeosetScaling // 'KGSC'
{
    

                                    //DWORD NrOfTracks;
    DWORD interpolationType;        //0 - None
                                    //1 - Linear
                                    //2 - Hermite
                                    //3 - Bezier
    DWORD globalSequenceId;
    Array<MdxScalingTrack> tracks;

    int getSize()
    {
        int mySize = 0;
        mySize += 2 * sizeof(DWORD);
        int count = tracks.size();
        mySize += count * (sizeof(DWORD) + sizeof(mdxFloat3));
        if(interpolationType > 1)
        {
            mySize += count * 2 * sizeof(mdxFloat3);
        }
        return mySize;
    }
};

struct mdxNode
{
  DWORD inclusiveSize;

  char  name[80];

  DWORD ObjectId;
  DWORD ParentId;
  DWORD Flags;                         //0        - Helper
                                       //#1       - DontInheritTranslation
                                       //#2       - DontInheritRotation
                                       //#4       - DontInheritScaling
                                       //#8       - Billboarded
                                       //#16      - BillboardedLockX
                                       //#32      - BillboardedLockY
                                       //#64      - BillboardedLockZ
                                       //#128     - CameraAnchored
                                       //#256     - Bone
                                       //#512     - Light
                                       //#1024    - EventObject
                                       //#2048    - Attachment
                                       //#4096    - ParticleEmitter
                                       //#8192    - CollisionShape
                                       //#16384   - RibbonEmitter
                                       //#32768   - Unshaded / EmitterUsesMdl
                                       //#65536   - SortPrimitivesFarZ / EmitterUsesTga
                                       //#131072  - LineEmitter
                                       //#262144  - Unfogged
                                       //#524288  - ModelSpace
                                       //#1048576 - XYQuad
    Array<MdxGeosetTranslation> translationTrack;
    Array<MdxGeosetRotation>    rataionTrack;
    Array<MdxGeosetScaling>     scalingTrack;

    int getSize()
    {
        return inclusiveSize;
    }

};


struct MdxBone     // 'BONE'
{
    mdxNode node;
    DWORD GeosetId;
    DWORD GeosetAnimationId;
    
    int getSize()
    {
        int mySize = 0;
        mySize += node.getSize() + 2 * sizeof(DWORD);
        return mySize;
    }
};

struct MdxBoneSet
{
    Array<MdxBone> bones;
};

//+-----------------------------------------------------------------------------
//| Pivot points
//+-----------------------------------------------------------------------------
struct MdxPivotPointSet  // 'PIVT'
{
                                // DWORD ChunkSize;
    Array<mdxFloat3> positions; // //NrOfPivotPoints = ChunkSize / 12
};


class mdxModel;

class MdxParser
{
    friend class mdxModel;
public:
    MdxParser(void);
    ~MdxParser(void);
public:
    bool    load(String& name);
protected:
    bool    loadModelInfoSection(int sectionSize);
    bool    loadBoundingBox(mdxBoundingBox& box);
    bool    loadSecquencesSection(int sectionSize);
    bool    loadSecquence(MdxSequence& sequence);
    bool    loadGlobalSequencesSection(int sectionSize);
    bool    loadMaterialSeciton(int sectionSize);
    bool    loadMaterial(MdxMaterial& material);
    bool    loadMaterialLayerSection(MdxMaterial& material);
    bool    loadMaterialLayer(MdxMaterialLayer& materialLayer);
    bool    loadMaterialLayerAlpha(MdxMaterialAlpha& materialAlpha);
    bool    loadMaterialAlphaScalingTrack(mdxScalingTrack& tarck, bool hasInterpolation);
    bool    loadMaterialTextrueId(MdxMaterialTextureId& materialTextureId);
    bool    loadMaterialTextureScalingTrack(mdxScalingTrack& tarck, bool hasInterpolation);
    bool    loadTexturesSection(int sectionSize);
    bool    loadTexture(MdxTexture& texture);
    bool    loadGeometricSection(int sectionSize);
    bool    loadGeometrics(MdxGeometric& geometric);
    bool    loadGeometAnimationSection(int sectionSize);
    bool    loadGeometAnimation(MdxGeometAnimation& geometAnimation);
    bool    loadGeometAnimationAlpha(MdxGeometAlpha& alphaTrack);
    bool    loadGeometAnimationColor(MdxGeometColor& colorTrack);
    bool    loadBoneSection(int sectionSize);
    bool    loadBone(MdxBone& bone);
    bool    loadNode(mdxNode& node);
    bool    loadAttachmentSection(int sectionSize);
    bool    loadPivotPointSection(int sectionSize);
protected:
    File*                   _pReader;
    MdxInfo                 _mdxInfo;
    MdxSequenceSet          _mdxSequenceSet;
    MdxGolbalSequenceSet    _mdxGolbalSequenceSet;
    MdxMaterialSet          _mdxMaterialSet;
    MdxTextureSet           _mdxTextureSet;
    MdxGeometricSet         _mdxGeometriSet;
    MdxGeometAnimationSet   _mdxGeometAnimationSet;
    MdxBoneSet              _mdxBoneSet;
    MdxPivotPointSet        _mdxPivotPointSet;
};

} // LSH
